package edu.uts.reekie.designer.model;

import org.eclipse.draw2d.Graphics;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.ui.views.properties.PropertyDescriptor;

/*
 * 
 * TODO: expose directions as property sheet (combo box - see connections in shapes example)
 * TODO: add labels for decorations
 * TODO: 
 */
public class AbstractConnector extends ViewElement {
	/**
	 * Used for indicating that a Connection with solid line style should be
	 * created.
	 * 
	 * @see org.eclipse.gef.examples.shapes.parts.ShapeEditPart#createEditPolicies()
	 */
	public static final int UNIDIRECTIONAL_CONNECTION = 0;
	/**
	 * Used for indicating that a Connection is bidirectional
	 */
	public static final int BIDIRECTIONAL_CONNECTION = 1;

	public static final int LABEL_BI_DIRECTIONAL = 0;
	public static final int LABEL_SOURCE_TO_TARGET = 1;
	public static final int LABEL_TARGET_TO_SOURCE = 2;

	/** Property ID to use when the line style of this connection is modified. */
	public static final String PROPERTY_DIRECTION = "Direction";
	public static final String PROPERTY_LABEL = "Label";
	public static final String PROPERTY_SOURCE_TO_TARGET_LABEL = "Source to Target Label";
	public static final String PROPERTY_TARGET_TO_SOURCE_LABEL = "Target to Source Label";

	private static final IPropertyDescriptor[] descriptors = new IPropertyDescriptor[4];
	private static final long serialVersionUID = 1;

	/** True, if the connection is attached to its endpoints. */
	private boolean isConnected;

	/** Direction ***/
	private int direction = UNIDIRECTIONAL_CONNECTION;

	/** Line drawing style for this connection. */
	private int lineStyle = Graphics.LINE_SOLID;

	/** Connection's source endpoint. */
	private ViewNode source;
	/** Connection's target endpoint. */
	private ViewNode target;

	/*
	 * Labels
	 */
	private String label = null;
	private String targetToSourceLabel = null;
	private String sourceToTargetLabel = null;

	static {

		descriptors[0] = new PropertyDescriptor(PROPERTY_DIRECTION,
				PROPERTY_DIRECTION);
		descriptors[1] = new PropertyDescriptor(PROPERTY_LABEL, PROPERTY_LABEL);
		descriptors[2] = new PropertyDescriptor(
				PROPERTY_TARGET_TO_SOURCE_LABEL,
				PROPERTY_TARGET_TO_SOURCE_LABEL);
		descriptors[3] = new PropertyDescriptor(
				PROPERTY_SOURCE_TO_TARGET_LABEL,
				PROPERTY_SOURCE_TO_TARGET_LABEL);
	}

	public AbstractConnector() {

	}

	/**
	 * Create a (solid) connection between two distinct shapes.
	 * 
	 * @param source
	 *            a source endpoint for this connection (non null)
	 * @param target
	 *            a target endpoint for this connection (non null)
	 * @throws IllegalArgumentException
	 *             if any of the parameters are null or source == target
	 * @see #setLineStyle(int)
	 */
	public AbstractConnector(ViewNode source, ViewNode target) {
		reconnect(source, target);
	}

	/**
	 * Disconnect this connection from the shapes it is attached to.
	 */
	public void disconnect() {
		if (isConnected) {
			source.removeConnection(this);
			target.removeConnection(this);
			isConnected = false;
		}
	}

	public int getDirection() {
		return direction;
	}

	/**
	 * Returns the descriptor for the lineStyle property
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
	 */
	public IPropertyDescriptor[] getPropertyDescriptors() {
		return descriptors;
	}

	/**
	 * Returns the lineStyle as String for the Property Sheet
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object)
	 */
	public Object getPropertyValue(Object id) {
		if (id.equals(PROPERTY_DIRECTION)) {
			return new Integer(direction);
		}
		if (id.equals(PROPERTY_LABEL)) {
			return label;
		}
		if (id.equals(PROPERTY_SOURCE_TO_TARGET_LABEL)) {
			return sourceToTargetLabel;
		}
		if (id.equals(PROPERTY_TARGET_TO_SOURCE_LABEL)) {
			return targetToSourceLabel;
		}
		return super.getPropertyValue(id);
	}

	/**
	 * Returns the source endpoint of this connection.
	 * 
	 * @return a non-null Shape instance
	 */
	public ViewNode getSource() {
		return source;
	}

	/**
	 * Returns the target endpoint of this connection.
	 * 
	 * @return a non-null Shape instance
	 */
	public ViewNode getTarget() {
		return target;
	}

	/**
	 * Reconnect this connection. The connection will reconnect with the shapes
	 * it was previously attached to.
	 */
	public void reconnect() {
		if (!isConnected) {
			source.addConnection(this);
			target.addConnection(this);
			isConnected = true;
		}
	}

	/**
	 * Reconnect to a different source and/or target shape. The connection will
	 * disconnect from its current attachments and reconnect to the new source
	 * and target.
	 * 
	 * @param newSource
	 *            a new source endpoint for this connection (non null)
	 * @param newTarget
	 *            a new target endpoint for this connection (non null)
	 * @throws IllegalArgumentException
	 *             if any of the paramers are null or newSource == newTarget
	 */
	public void reconnect(ViewNode newSource, ViewNode newTarget) {
		if (newSource == null || newTarget == null || newSource == newTarget) {
			throw new IllegalArgumentException();
		}
		disconnect();
		this.source = newSource;
		this.target = newTarget;
		reconnect();
	}

	/**
	 * Set the line drawing style of this connection.
	 * 
	 * @param lineStyle
	 *            one of following values: Graphics.LINE_DASH or
	 *            Graphics.LINE_SOLID
	 * @see Graphics#LINE_DASH
	 * @see Graphics#LINE_SOLID
	 * @throws IllegalArgumentException
	 *             if lineStyle does not have one of the above values
	 */
	public void setDirection(int newdirection) {
		if (newdirection != UNIDIRECTIONAL_CONNECTION
				&& newdirection != BIDIRECTIONAL_CONNECTION) {
			throw new IllegalArgumentException();
		}

		this.direction = newdirection;
		firePropertyChange(PROPERTY_DIRECTION, null,
				new Integer(this.direction));
	}

	/**
	 * Sets the lineStyle based on the String provided by the PropertySheet
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#setPropertyValue(java.lang.Object,
	 *      java.lang.Object)
	 */
	public void setPropertyValue(Object id, Object value) {
		if (id.equals(PROPERTY_DIRECTION))
			setDirection(new Integer(1).equals(value) ? BIDIRECTIONAL_CONNECTION
					: UNIDIRECTIONAL_CONNECTION);
		else if (id.equals(PROPERTY_LABEL))
			setLabel((String) value);
		else if (id.equals(PROPERTY_TARGET_TO_SOURCE_LABEL))
			setTargetToSourceLabel((String) value);
		else if (id.equals(PROPERTY_SOURCE_TO_TARGET_LABEL))
			setSourceToTargetLabel((String) value);
		else
			super.setPropertyValue(id, value);
	}

	public String getLabel() {
		return label;
	}

	public void setLabel(String label) {
		this.label = label;
		firePropertyChange(PROPERTY_LABEL, null, this.label);
	}

	public String getTargetToSourceLabel() {
		return targetToSourceLabel;
	}

	public void setTargetToSourceLabel(String targetToSourceLabel) {
		this.targetToSourceLabel = targetToSourceLabel;
		firePropertyChange(PROPERTY_TARGET_TO_SOURCE_LABEL, null,
				this.targetToSourceLabel);
	}

	public String getSourceToTargetLabel() {
		return sourceToTargetLabel;
	}

	public void setSourceToTargetLabel(String sourceToTargetLabel) {
		this.sourceToTargetLabel = sourceToTargetLabel;
		firePropertyChange(PROPERTY_SOURCE_TO_TARGET_LABEL, null,
				this.sourceToTargetLabel);
	}
}
